package top.xana.acg.common.base.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.CallSuper;
import androidx.annotation.IdRes;
import androidx.annotation.NavigationRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import androidx.navigation.NavGraph;
import androidx.navigation.NavHost;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigator;
import androidx.navigation.fragment.FragmentNavigator;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Map;

/**
 * Created by xana on 2022/3/24 0024
 * Describe: cv4096@qq.com
 */


public class NavHostFragment extends androidx.navigation.fragment.NavHostFragment {
    @NonNull
    @Override
    protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
        int id = 0;
        try {
            Class<NavHostFragment> clz = (Class<NavHostFragment>) getClass();
            Method method = clz.getSuperclass().getDeclaredMethod("getContainerId");
            method.setAccessible(true);
            id = (int) method.invoke(this);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return new Fn(requireContext(), getChildFragmentManager(),
                id);
    }

    @Navigator.Name("fragment")
    public static class Fn extends FragmentNavigator {

        public Fn(@NonNull Context context, @NonNull FragmentManager manager, int containerId) {
            super(context, manager, containerId);
            init();
            fm = manager;
            ctx = context;
            cid = containerId;
        }

        FragmentManager fm;
        Context ctx;
        int cid;

        ArrayDeque<Integer> bs;
        private Class<Fn> mClz;


        Method generateBackStackName;

        private void init() {
            try {
                mClz = (Class<Fn>) getClass();
                Field field = mClz.getSuperclass().getDeclaredField("mBackStack");
                field.setAccessible(true);
                bs = (ArrayDeque<Integer>) field.get(this);

                generateBackStackName = mClz.getSuperclass().getDeclaredMethod("generateBackStackName", Integer.class, Integer.class);
                generateBackStackName.setAccessible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        @Nullable
        @Override
        public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
                                       @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
            if (fm.isStateSaved()) {
                return null;
            }
            String className = destination.getClassName();
            if (className.charAt(0) == '.') {
                className = ctx.getPackageName() + className;
            }
            final Fragment frag = instantiateFragment(ctx, fm,
                    className, args);
            frag.setArguments(args);
            final FragmentTransaction ft = fm.beginTransaction();

            int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
            int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
            int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
            int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
            if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
                enterAnim = enterAnim != -1 ? enterAnim : 0;
                exitAnim = exitAnim != -1 ? exitAnim : 0;
                popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
                popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
                ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
            }

            ft.replace(cid, frag);
            ft.setPrimaryNavigationFragment(frag);

            final @IdRes int destId = destination.getId();
            final boolean initialNavigation = bs.isEmpty();
            // TODO Build first class singleTop behavior for fragments
            final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                    && navOptions.shouldLaunchSingleTop()
                    && bs.peekLast() == destId;

            boolean isAdded;
            if (initialNavigation) {
                isAdded = true;
            } else if (isSingleTopReplacement) {
                // Single Top means we only want one instance on the back stack
                if (bs.size() > 1) {
                    // If the Fragment to be replaced is on the FragmentManager's
                    // back stack, a simple replace() isn't enough so we
                    // remove it from the back stack and put our replacement
                    // on the back stack in its place

                    try{
                        fm.popBackStack(
                                (String) generateBackStackName.invoke(this, bs.size(), bs.peekLast()),
                                FragmentManager.POP_BACK_STACK_INCLUSIVE);
                        ft.addToBackStack((String) generateBackStackName.invoke(this, bs.size(), destId));

                    }catch (Exception e){
                        e.printStackTrace();
                    }


                }
                isAdded = false;
            } else {
                try{
                    ft.addToBackStack((String) generateBackStackName.invoke(this, bs.size()+1, destId));
                }catch (Exception e){
                    e.printStackTrace();
                }
                isAdded = true;
            }
            if (navigatorExtras instanceof Extras) {
                Extras extras = (Extras) navigatorExtras;
                for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
                    ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
                }
            }
            ft.setReorderingAllowed(true);
            ft.commit();
            // The commit succeeded, update our view of the world
            if (isAdded) {
                bs.add(destId);
                return destination;
            } else {
                return null;
            }
        }
    }
}